﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Management;
using System.Xml;
using Tum.CollabXT.DocxGen.Resources;
using TUM.Mustertexte_Parser;


namespace Tum.CollabXT.DocxGen
{
    public class DocxGen : IToolProvider
    {
        public Dictionary<string, object> ExtendedData
        {
            get { return _ExtendedData; }
        }

        private readonly Dictionary<string, object> _ExtendedData = new Dictionary<string, object>();

        /// <summary>
        /// Name of the project.
        /// </summary>
        public string ProjectName { get; set; }

        /// <summary>
        /// Company, creating the product.
        /// </summary>
        public string Company { get; set; }

        /// <summary>
        /// Author of the templates.
        /// </summary>
        public string Author { get; set; }


        /// <summary>
        /// Output path for the generated product templates.
        /// </summary>
        public string OutputPath { get; set; }

        /// <summary>
        /// Path of the templates to be used for the conversion. (MasterTemplate.{docx|dotx})
        /// </summary>
        public string TemplatePath { get; set; }

        /// <summary>
        /// Path of template for texts within the generated document (Mustertext). In VMXT-Mustertext-Syntax.
        /// </summary>
        public string TemplateTextsPath { get; set; }

        public Dictionary<string, Tree<TemplateSerializationItem>> ProductsToGenerated { get; private set; }


        private Log _Log;
        internal IProcessProvider ProcessProvider;


        private const string DocMasterTemplateFileName = "MasterTemplate.docx";
        private const string DotMasterTemplateFileName = "MasterTemplate.dotx";

        /// <summary>
        /// Initialize the provider.
        /// </summary>
        /// <param name="processProvider">The process provider to be used for input data.</param>
        /// <param name="log">Log to be used.</param>
        public void Initialize(IProcessProvider processProvider, Log log)
        {
            ProcessProvider = processProvider;
            _Log = log;

            if (ProductsToGenerated == null)
                ProductsToGenerated = new Dictionary<string, Tree<TemplateSerializationItem>>();

            if (Author == null)
            {
                //Get logged on user as author
                Author = Environment.UserName;
            }

            if (Company == null)
            {
                //Get company, system is licensed to
                try
                {
                    var osMgmt = new ManagementClass("Win32_OperatingSystem");
                    foreach (var curOsInstance in osMgmt.GetInstances())
                        Company = curOsInstance["Organization"] as string;
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// Processes the data and generates the document templates.
        /// </summary>
        public void Process()
        {
            #region Preparations

            if (ProcessProvider == null)
                throw new DocxGenException("ProcessProvider has not been set. Call Initialize before.");

            if (!Directory.Exists(TemplatePath))
                throw new DocxGenException("Template folder does not exist");

            if (!OutputPath.EndsWith("/", StringComparison.OrdinalIgnoreCase) && !OutputPath.EndsWith("\\", StringComparison.OrdinalIgnoreCase))
                OutputPath += "/";

            if (!TemplatePath.EndsWith("/", StringComparison.OrdinalIgnoreCase) && !TemplatePath.EndsWith("\\", StringComparison.OrdinalIgnoreCase))
                TemplatePath += "/";

            Directory.CreateDirectory(OutputPath);

            #endregion

            //Initialize DocumentGenerator instance
            var docGen = new DocumentGenerator(_Log);

            docGen.OutputPath = OutputPath;
            docGen.ProjectName = ProjectName;
            docGen.Company = Company;
            docGen.Author = Author;

            //Check if template files exist
            if (!File.Exists(TemplatePath + DocMasterTemplateFileName))
                throw new DocxGenException(string.Format(Language.Error_TemplateFileNotFound, DocMasterTemplateFileName));
            if (!File.Exists(TemplatePath + DotMasterTemplateFileName))
                throw new DocxGenException(string.Format(Language.Error_TemplateFileNotFound, DotMasterTemplateFileName));

            docGen.TemplateDocxPath = TemplatePath + DocMasterTemplateFileName;
            docGen.TemplateDotxPath = TemplatePath + DotMasterTemplateFileName;

            docGen.TemplateTextsPath = TemplateTextsPath;

            //Generate documents
            docGen.GenerateDocuments(ProcessProvider, ProductsToGenerated);
        }

        /// <summary>
        /// Gets the name of the XAML config page.
        /// </summary>
        /// <returns>XAML config page name.</returns>
        public string GetConfigPageName()
        {
            return "DocxConfigPage.xaml";
        }

        /// <summary>
        /// Gets the name of the DocX provider.
        /// </summary>
        /// <returns>DocX provider name.</returns>
        public static string GetProviderName()
        {
            return Language.ProviderInfo_Name;
        }

        /// <summary>
        /// Gets the provider's description.
        /// </summary>
        /// <returns>Provider description.</returns>
        public static string GetProviderDescription()
        {
            return Language.ProviderInfo_Description;
        }

        /// <summary>
        /// Gets the provider's author.
        /// </summary>
        /// <returns>Provider author.</returns>
        public static string GetProviderAuthor()
        {
            return "© 2011 TU München";
        }


        public void Serialize(XmlNode outputParentNode)
        {
            var xmlDoc = outputParentNode.OwnerDocument;

            XmlNode settingsNode = xmlDoc.CreateElement("settings");
            outputParentNode.AppendChild(settingsNode);

            var outputPathAttribute = xmlDoc.CreateAttribute("outputPath");
            outputPathAttribute.Value = OutputPath;
            settingsNode.Attributes.Append(outputPathAttribute);

            var templatePathAttribute = xmlDoc.CreateAttribute("templatePath");
            templatePathAttribute.Value = TemplatePath;
            settingsNode.Attributes.Append(templatePathAttribute);

            var textTemplatePathAttribute = xmlDoc.CreateAttribute("textTemplatePath");
            textTemplatePathAttribute.Value = TemplateTextsPath;
            settingsNode.Attributes.Append(textTemplatePathAttribute);

            var projectNameAttribute = xmlDoc.CreateAttribute("projectName");
            projectNameAttribute.Value = ProjectName;
            settingsNode.Attributes.Append(projectNameAttribute);

            var companyNameAttribute = xmlDoc.CreateAttribute("company");
            companyNameAttribute.Value = Company;
            settingsNode.Attributes.Append(companyNameAttribute);

            var authorNameAttribute = xmlDoc.CreateAttribute("author");
            authorNameAttribute.Value = Author;
            settingsNode.Attributes.Append(authorNameAttribute);

            //Write products
            XmlNode selectedProductsNode = xmlDoc.CreateElement("products");
            settingsNode.AppendChild(selectedProductsNode);

            foreach (var curProd in ProductsToGenerated)
            {
                XmlNode newProductNode = xmlDoc.CreateElement("product");
                selectedProductsNode.AppendChild(newProductNode);

                var productIdAttribute = xmlDoc.CreateAttribute("id");
                productIdAttribute.Value = curProd.Key;
                newProductNode.Attributes.Append(productIdAttribute);

                //Write templates
                Serialize_WriteTemplateTexts(newProductNode, curProd.Value);
            }
        }

        private static void Serialize_WriteTemplateTexts(XmlNode parentNode, TreeItem<TemplateSerializationItem> templateTree)
        {
            foreach (var curTreeNode in templateTree.Children)
            {
                string nodeName;
                if (curTreeNode.Value is TemplateSerializationTopic)
                    nodeName = "topic";
                else if (curTreeNode.Value is TemplateSerializationText)
                    nodeName = "text";
                else
                    continue;

                XmlNode curElementNode = parentNode.OwnerDocument.CreateElement(nodeName);
                parentNode.AppendChild(curElementNode);

                var curNameAttribute = parentNode.OwnerDocument.CreateAttribute("name");
                curNameAttribute.Value = curTreeNode.Value.Name;
                curElementNode.Attributes.Append(curNameAttribute);

                Serialize_WriteTemplateTexts(curElementNode, curTreeNode);
            }
        }

        private static string SafeGetAttributeValue(XmlNode node, string attributeName)
        {
            var item = node.Attributes.GetNamedItem(attributeName);
            return item != null ? item.Value : string.Empty;
        }

        public void Deserialize(XmlNode inputParentNode)
        {
            var settingsNode = inputParentNode.SelectSingleNode("settings");
            ProductsToGenerated = new Dictionary<string, Tree<TemplateSerializationItem>>();

            OutputPath = SafeGetAttributeValue(settingsNode, "outputPath");

            TemplatePath = SafeGetAttributeValue(settingsNode, "templatePath");
            if (!Directory.Exists(TemplatePath))
                TemplatePath = null;

            TemplateTextsPath = SafeGetAttributeValue(settingsNode, "textTemplatePath");
            if (!File.Exists(TemplateTextsPath))
                TemplateTextsPath = null;

            ProjectName = SafeGetAttributeValue(settingsNode, "projectName");
            Company = SafeGetAttributeValue(settingsNode, "company");
            Author = SafeGetAttributeValue(settingsNode, "author");

            var selectedProductNodes = settingsNode.SelectNodes("products/product");
            foreach (XmlNode curSelectedProd in selectedProductNodes)
            {
                var templateTree = new Tree<TemplateSerializationItem>();
                Deserialize_LoadTemplateTexts(curSelectedProd, templateTree);

                var prodIdAttr = curSelectedProd.Attributes.GetNamedItem("id");
                if (prodIdAttr != null)
                    ProductsToGenerated.Add(prodIdAttr.Value.ToLower(), templateTree);
            }
        }

        private static void Deserialize_LoadTemplateTexts(XmlNode parentNode, TreeItem<TemplateSerializationItem> templateTree)
        {
            foreach (XmlNode curNode in parentNode.ChildNodes)
            {
                XmlAttribute curContentAttr = curNode.Attributes["name"];
                if(curContentAttr == null)
                    continue;

                TemplateSerializationItem curNodeItem;
                if (curNode.Name.Equals("topic", StringComparison.InvariantCultureIgnoreCase))
                    curNodeItem = new TemplateSerializationTopic();
                else if(curNode.Name.Equals("text", StringComparison.InvariantCultureIgnoreCase))
                    curNodeItem = new TemplateSerializationText();
                else
                    continue;

                curNodeItem.Name = curContentAttr.Value;
                Deserialize_LoadTemplateTexts(curNode, templateTree.AppendChild(curNodeItem));
            }
        }
    }

    public class TemplateSerializationItem
    {
        public string Name;
    }

    public class TemplateSerializationTopic : TemplateSerializationItem
    {
    }

    public class TemplateSerializationText : TemplateSerializationItem
    {
    }
}